package com.yanfan.energybilling.service.impl;

import cn.hutool.core.date.DateUtil;
import com.yanfan.community.building.domain.CommunityBuilding;
import com.yanfan.community.building.service.ICommunityBuildingService;
import com.yanfan.energybilling.domain.TDEnergyDeductionRecord;
import com.yanfan.energybilling.domain.dto.TDEnergyDeductionRecordDto;
import com.yanfan.energybilling.domain.vo.EnergyCostStatisticsVo;
import com.yanfan.energybilling.domain.vo.EnergyStatisticsVo;
import com.yanfan.energybilling.domain.vo.GroupByEnergyTypeVo;
import com.yanfan.energybilling.domain.vo.StaticRequestParamVo;
import com.yanfan.energybilling.service.ITDEnergyDeductionRecordService;
import com.yanfan.energybilling.tdengine.TDEnergyDeductionRecordMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class TDEnergyDeductionRecordServiceImpl implements ITDEnergyDeductionRecordService {

    @Value("${spring.datasource.druid.tdengine-server.dbName}")
    private String dbName;

    @Autowired(required = false)
    private TDEnergyDeductionRecordMapper tdEnergyDeductionRecordMapper;
    @Autowired
    private ICommunityBuildingService communityBuildingService;

    @Override
    public TDEnergyDeductionRecord selectLatestRecord(String serialNumber, String thingsModelId) {
        int i = tdEnergyDeductionRecordMapper.tableExit(dbName);
        if (i <= 0) {
            return null;
        }
        return tdEnergyDeductionRecordMapper.selectLatestRecord(dbName, serialNumber, thingsModelId);
    }

    @Override
    public Long selectTimeRangeLast(Long begin,Long end) {
        return tdEnergyDeductionRecordMapper.selectTimeRangeLast(dbName, begin, end);
    }

    @Override
    public int insertRecord(TDEnergyDeductionRecord record) {
        return tdEnergyDeductionRecordMapper.save(dbName, record);
    }

    @Transactional
    @Override
    public int insertBatchRecord(TDEnergyDeductionRecordDto records) {
        List<TDEnergyDeductionRecord> list = records.getList();
        int pageSize = 1000;
        int totalResult = 0;

        // 计算总页数
        int totalPages = (int) Math.ceil((double) list.size() / pageSize);

        // 使用IntStream处理每页数据
        for (int i = 0; i < totalPages; i++) {
            List<TDEnergyDeductionRecord> batch = list.stream()
                    .skip((long) i * pageSize)
                    .limit(pageSize)
                    .collect(Collectors.toList());

            // 创建批次DTO
            TDEnergyDeductionRecordDto batchDto = new TDEnergyDeductionRecordDto();
            batchDto.setSerialNumber(records.getSerialNumber());
            batchDto.setList(batch);

            // 执行批量插入
            totalResult += tdEnergyDeductionRecordMapper.saveBatch(dbName, batchDto);
        }
        return totalResult;
    }

    @Override
    public List<EnergyStatisticsVo> buildingEnergyStatistics() {
        return tdEnergyDeductionRecordMapper.buildingEnergyStatistics(dbName);
    }

    @Override
    public List<EnergyCostStatisticsVo> energyCostStatisticsTimeWindow(StaticRequestParamVo param) {
        param.setBeginLong(param.getBegin().toInstant().getEpochSecond());
        param.setEndLong(param.getEnd().toInstant().getEpochSecond());
        param.setBeginStr(DateUtil.format(param.getBegin(), "yyyy-MM-dd")+ " 00:00:00");
        param.setEndStr(DateUtil.format(param.getEnd(), "yyyy-MM-dd") + " 23:59:59");
        setBuildingIds( param);
        List<EnergyCostStatisticsVo> energyCostStatisticsVos = tdEnergyDeductionRecordMapper.energyCostStatisticsTimeWindow(dbName, param);
        Date beginDate = param.getBegin();
        Date endDate = param.getEnd();
        String dateType = param.getDateType();
        generateDefaultData(energyCostStatisticsVos, beginDate, endDate,dateType);
        return energyCostStatisticsVos;
    }

    @Override
    public List<EnergyCostStatisticsVo> energyCostStatisticsTimeWindowWater(StaticRequestParamVo param) {
        //不需要传递单价类
        param.setTypeList(null);
        //不需要能源汇总的集合
        param.setEnergyTypeList(null);
        param.setBeginLong(param.getBegin().toInstant().getEpochSecond());
        param.setEndLong(param.getEnd().toInstant().getEpochSecond());
        param.setBeginStr(DateUtil.format(param.getBegin(), "yyyy-MM-dd")+ " 00:00:00");
        param.setEndStr(DateUtil.format(param.getEnd(), "yyyy-MM-dd") + " 23:59:59");
        List<EnergyCostStatisticsVo> energyCostStatisticsVos = tdEnergyDeductionRecordMapper.energyCostStatisticsTimeWindow(dbName, param);
        Date beginDate = param.getBegin();
        Date endDate = param.getEnd();
        String dateType = param.getDateType();
        generateDefaultData(energyCostStatisticsVos, beginDate, endDate,dateType);
        return energyCostStatisticsVos;
    }

    @Override
    public List<EnergyCostStatisticsVo> energyCostStatisticsTimeWindowsByDate(StaticRequestParamVo param) {
        param.setBeginLong(param.getBegin().toInstant().getEpochSecond());
        param.setEndLong(param.getEnd().toInstant().getEpochSecond());
        param.setBeginStr(DateUtil.format(param.getBegin(), "yyyy-MM-dd")+ " 00:00:00");
        param.setEndStr(DateUtil.format(param.getEnd(), "yyyy-MM-dd") + " 23:59:59");
        List<EnergyCostStatisticsVo> energyCostStatisticsVos = tdEnergyDeductionRecordMapper.energyCostStatisticsTimeWindowData(dbName, param);
        return energyCostStatisticsVos;
    }

    @Override
    public List<EnergyCostStatisticsVo> energyConsumeStatisticsTimeWindow(StaticRequestParamVo param) {
         param.setBeginLong(param.getBegin().toInstant().getEpochSecond());
        param.setEndLong(param.getEnd().toInstant().getEpochSecond());
        param.setBeginStr(DateUtil.format(param.getBegin(), "yyyy-MM-dd")+ " 00:00:00");
        param.setEndStr(DateUtil.format(param.getEnd(), "yyyy-MM-dd") + " 23:59:59");
        List<EnergyCostStatisticsVo> energyCostStatisticsVos = tdEnergyDeductionRecordMapper.energyConsumeStatisticsTimeWindow(dbName, param);
        Date beginDate = param.getBegin();
        Date endDate = param.getEnd();
        String dateType = param.getDateType();
        generateDefaultData(energyCostStatisticsVos, beginDate, endDate,dateType);
        return energyCostStatisticsVos;
    }

    /**
     * 生成每月默认数据
     */
    private void generateDefaultData(List<EnergyCostStatisticsVo> list, Date beginDate, Date endDate,String dateType) {
        LocalDate start = beginDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
        LocalDate end = endDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();

        switch (dateType){
            case "hour":
                // 按小时处理数据
                Set<LocalDate> existingHours = list.stream()
                        .map(item -> {
                            Date itemTime = item.getTime();
                            return itemTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
                        })
                        .collect(Collectors.toSet());

                // 获取开始时间的小时
                java.time.LocalDateTime hourCurrent = java.time.LocalDateTime.of(start, java.time.LocalTime.MIDNIGHT);
                // 获取结束时间的小时
                java.time.LocalDateTime hourEnd = java.time.LocalDateTime.of(end, java.time.LocalTime.MAX);

                while (!hourCurrent.isAfter(hourEnd)) {
                    if (!existingHours.contains(hourCurrent.toLocalDate())) {
                        Date hourDate = Date.from(hourCurrent.atZone(ZoneId.systemDefault()).toInstant());
                        list.add(new EnergyCostStatisticsVo(hourDate, BigDecimal.ZERO, BigDecimal.ZERO));
                    }
                    // 移动到下一个小时
                    hourCurrent = hourCurrent.plusHours(1);
                }
                break;
            case "day":
                // 预先创建已存在日期的Set，提高查找效率
                Set<LocalDate> existingDates = list.stream()
                        .map(item -> {
                            Date itemTime = item.getTime();
                            return itemTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
                        })
                        .collect(Collectors.toSet());

                LocalDate current = start;
                while (!current.isAfter(end)) {
                    if (!existingDates.contains(current)) {
                        list.add(new EnergyCostStatisticsVo(Date.from(current.atStartOfDay(ZoneId.systemDefault()).toInstant()), BigDecimal.ZERO, BigDecimal.ZERO));
                    }
                    current = current.plusDays(1);
                }
                break;
            case "month":
                // 按月处理数据
                Set<LocalDate> existingMonths = list.stream()
                        .map(item -> {
                            Date itemTime = item.getTime();
                            LocalDate localDate = itemTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
                            return localDate.withDayOfMonth(1); // 获取每月第一天作为标识
                        })
                        .collect(Collectors.toSet());

                // 调整起始时间为该月第一天
                LocalDate monthCurrent = start.withDayOfMonth(1);
                // 结束时间调整为该月末
                LocalDate monthEnd = end.withDayOfMonth(1);

                while (!monthCurrent.isAfter(monthEnd)) {
                    if (!existingMonths.contains(monthCurrent)) {
                        Date monthDate = Date.from(monthCurrent.atStartOfDay(ZoneId.systemDefault()).toInstant());
                        list.add(new EnergyCostStatisticsVo(monthDate, BigDecimal.ZERO, BigDecimal.ZERO));
                    }
                    // 移动到下一月
                    monthCurrent = monthCurrent.plusMonths(1);
                }
                break;
        }


    }

    @Override
    public List<EnergyCostStatisticsVo> energyCountAllStatisticsTimeWindow(StaticRequestParamVo param) {
        param.setBeginLong(param.getBegin().toInstant().getEpochSecond());
        param.setEndLong(param.getEnd().toInstant().getEpochSecond());
        param.setBeginStr(DateUtil.format(param.getBegin(), "yyyy-MM-dd")+ " 00:00:00");
        param.setEndStr(DateUtil.format(param.getEnd(), "yyyy-MM-dd") + " 23:59:59");
        setBuildingIds(param);
        return tdEnergyDeductionRecordMapper.energyCountAllStatisticsTimeWindow(dbName, param);
    }

    @Override
    public BigDecimal energyCost(StaticRequestParamVo param) {
        param.setBeginLong(param.getBegin().toInstant().getEpochSecond());
        param.setEndLong(param.getEnd().toInstant().getEpochSecond());
        param.setBeginStr(DateUtil.format(param.getBegin(), "yyyy-MM-dd")+ " 00:00:00");
        param.setEndStr(DateUtil.format(param.getEnd(), "yyyy-MM-dd") + " 23:59:59");
        setBuildingIds( param);
        BigDecimal bigDecimal = tdEnergyDeductionRecordMapper.energyCost(dbName, param);
        if (bigDecimal == null) {
            return BigDecimal.ZERO;
        }
        return bigDecimal;
    }

    public List<GroupByEnergyTypeVo> totalDifferenceValueGroupByEnergyType(String beginTime , String endTime) {
        return tdEnergyDeductionRecordMapper.totalDifferenceValueGroupByEnergyType(dbName,beginTime,endTime);
    }

    public List<GroupByEnergyTypeVo> buildEcharts(String timeType, List<String> energyTypeList, String benTime, String endTime,List<String> deviceNames) {
        return tdEnergyDeductionRecordMapper.buildEcharts(dbName,timeType,energyTypeList,benTime,endTime,deviceNames);
    }

    public List<GroupByEnergyTypeVo> sumDifferenceValueGroupByBuilding(String energyType) {
        return tdEnergyDeductionRecordMapper.sumDifferenceValueGroupByBuilding(dbName,energyType);
    }

    private void setBuildingIds(StaticRequestParamVo param) {
        if(Objects.nonNull(param.getBuildingId())){
            List<CommunityBuilding> communityBuildings = communityBuildingService.selectChildBuildingListWithOwnerByParentId(param.getBuildingId());
            param.setBuildingIdList(communityBuildings.stream().map(CommunityBuilding::getBuildingId).collect(Collectors.toList()));
        }
    }
}
